/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.benmei.ntlive.socket.server.netty.handler;

import com.benmei.ntlive.common.ErrorResult;
import com.benmei.ntlive.constant.Action;
import com.benmei.ntlive.dto.TcpRequest;
import com.benmei.ntlive.dto.TcpRequestBody;
import com.benmei.ntlive.dto.TcpRequestHead;
import com.benmei.ntlive.exception.NtClientException;
import com.benmei.ntlive.socket.server.netty.ChannelRepository;
import com.benmei.ntlive.utils.JsonUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.Map;

/**
 * NtliveServerHandler
 *
 * @author Peter
 * @date 2017-08-17
 */
@Component
@Qualifier("ntliveServerHandler")
@ChannelHandler.Sharable
public class NtliveServerHandler extends ChannelHandlerAdapter {

    @Autowired
    private ChannelRepository channelRepository;

    private static Logger logger = LoggerFactory.getLogger(NtliveServerHandler.class);

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Assert.notNull(this.channelRepository, "[Assertion failed] - ChannelRepository is required; it must not be null");

        ctx.fireChannelActive();
        logger.debug(ctx.channel().remoteAddress().toString());
        String channelKey = ctx.channel().remoteAddress().toString();
        channelRepository.put(channelKey, ctx.channel());

        ctx.writeAndFlush("Your channel key is " + channelKey + "\n\r");

        logger.debug("Binded Channel Count is " + this.channelRepository.size());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String stringMessage = (String) msg;
        try {
            // JSON格式校验
            if (JsonUtil.isValidJSON(stringMessage)) {
                ErrorResult res = new ErrorResult(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name(), null);
                ctx.channel().writeAndFlush(res.toString());
            }
            // 头部校验
            TcpRequest req = JsonUtil.toObject(stringMessage, TcpRequest.class);
            validateTcpHead(req.getHead());

            // action 校验
            Map<String, Object> body = req.getBody();
            Object objAction = body.get("action");
            if (objAction == null) {
                throw new NtClientException("Invalid data format:action is required");
            }
            if (!String.class.isInstance(objAction)) {
                throw new NtClientException("Invalid data format:action must be string");
            }
            String action = (String) objAction;
            if (!Action.isValidateAction(action)) {
                throw new NtClientException("Invalid action value '" + action + "'");
            }
            // 建立会话
        } catch (NtClientException e) {
            ctx.channel().writeAndFlush(e.toJson());
        }

        logger.debug(stringMessage);

        String[] splitMessage = stringMessage.split("::");

        if (splitMessage.length != 2) {
            ctx.channel().writeAndFlush(stringMessage + "\n\r");
            return;
        }

        if (channelRepository.get(splitMessage[0]) != null) {
            channelRepository.get(splitMessage[0]).writeAndFlush(splitMessage[1] + "\n\r");
        }
    }

    private void validateTcpHead(TcpRequestHead head) throws NtClientException {
        if (head == null) {
            throw new NtClientException("Invalid data format:head is required");
        }
        String apiVersion = head.getNtApiVersion();
        if (StringUtils.isBlank(apiVersion)) {
            throw new NtClientException("Invalid data format:ntApiVersion is required");
        }

        Long timestamp = head.getNtTimestamp();
        if (timestamp == null) {
            throw new NtClientException("Invalid data format:ntTimestamp is required");
        }

        String language = head.getNtClientLanguage();
        if (StringUtils.isBlank(language)) {
            throw new NtClientException("Invalid data format:ntClientLanguage is required");
        }

        String signature = head.getNtSignature();
        if (StringUtils.isBlank(signature)) {
            throw new NtClientException("Invalid data format:ntSignature is required");
        }

        String token = head.getNtToken();
        if (StringUtils.isBlank(token)) {
            throw new NtClientException("Invalid data format:token is required");
        }

        Integer osType = head.getNtClientOSType();
        if (osType == null) {
            throw new NtClientException("Invalid data format:ntClientOSType is required");
        }

        Integer clientType = head.getNtClientType();
        if (clientType == null) {
            throw new NtClientException("Invalid data format:ntClientType is required");
        }

        Integer role = head.getNtClientRole();
        if (role == null) {
            throw new NtClientException("Invalid data format:ntClientRole is required");
        }


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error(cause.getMessage(), cause);
        //ctx.close();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        Assert.notNull(this.channelRepository, "[Assertion failed] - ChannelRepository is required; it must not be null");

        String channelKey = ctx.channel().remoteAddress().toString();
        this.channelRepository.remove(channelKey);

        logger.debug("Binded Channel Count is " + this.channelRepository.size());
    }

    public void setChannelRepository(ChannelRepository channelRepository) {
        this.channelRepository = channelRepository;
    }
}
